package de.XPortLib;


import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

import de.XPortLib.Exceptions.TCPCommunicationException;

/**
 * @author olz
 * 
 * This class does the TCP/IP work to operate the XPort Most of the get and set
 * methods herein follow the same pattern: Establish a connection, prepare a
 * command, send this command and disconnect.
 *  
 */
class Communication {

    /** connection to XPort */
    protected Socket socket = null;

    /** dis contains data provided by XPort */
    protected BufferedReader dis = null;
    
    protected DataInputStream dis2 =null;
    
    /** dos contains data sent to XPort */
    protected DataOutputStream dos=null;
	
	/** sves the connection state */
	protected boolean connected = false;
    
    /**
     * This method converts the given String ipa_str and the int port into a
     * useable TCP/IP address and then calls a helper method tcpip_connect() to
     * establish a connection to the XPort
     * 
     * @param ipa_str
     * @param port
     * @throws TCPCommunicationException
     */
    public void connect(String ipa_str, int port) throws TCPCommunicationException {
        InetAddress ipa = null;
		if (!connected){
			try {
				ipa = InetAddress.getByName(ipa_str);
			} catch (UnknownHostException Ex) {
				throw new TCPCommunicationException("Unknown Host: " + ipa_str);
			}
			try {
				tcpip_connect(ipa, port);
				connected = true;
			} catch (Exception Ex) {
				throw new TCPCommunicationException(Ex.getLocalizedMessage());
			}
		}
    }

    /**
     * this method will end the current connection to the XPort
     * 
     * @throws TCPCommunicationException
     */
    public synchronized void disconnect() throws TCPCommunicationException {
		if (connected){
			try {
				tcpip_disconnect();
			}
			catch (TCPCommunicationException ex) {
				throw ex;
			}
			finally {
				connected = false;	
			}
		}
    }

    /**
     * uses a TCP/IP connection to send a String to the XPort
     * 
     * @param sendString
     * @throws TCPCommunicationException
     */
    public synchronized void send(String sendString) throws TCPCommunicationException {
        System.out.println(sendString);
        try {
            dos.writeBytes(sendString);//, 0, sendString.length());
            // write data to dos as String starting at String[0]
            // and ending at String[String.length()]
        } catch (Exception Ex) {
			connected = false;
            throw new TCPCommunicationException(Ex.getLocalizedMessage());
        }
        try {
            dos.flush(); // write dos to XPort
        } catch (Exception Ex) {
			connected = false;
            throw new TCPCommunicationException(Ex.getLocalizedMessage());
        }
        return;
    }

    /**
     * uses a TCP/IP connection to send a String to the XPort
     * 
     * @param sendByte
     * @throws TCPCommunicationException
     */
    public synchronized void send(int sendByte[]) throws TCPCommunicationException {
        try {
        	byte[] data = new byte[sendByte.length];
        	for (int i = 0; i < sendByte.length; i++) {
        		data[i] = (byte) sendByte[i];
            }
        	dos.write(data);
        } catch (Exception Ex) {
			connected = false;
            throw new TCPCommunicationException(Ex.getLocalizedMessage());
        }
        try {
            dos.flush(); // write dos to XPort
        } catch (Exception Ex) {
			connected = false;
            throw new TCPCommunicationException(Ex.getLocalizedMessage());
        }
    }

    /**
     * closes the TCP/IP connection
     * 
     * @throws TCPCommunicationException
     */
    private void tcpip_disconnect() throws TCPCommunicationException {
        if (socket.isConnected()) {
            try {
                dis.close(); // close input and output stream
                dos.close(); // as well as the socket
                socket.close();
            } catch (Exception Ex) {
                //throw new TCPCommunicationException(Ex.getLocalizedMessage());
                throw new TCPCommunicationException("Error on disconnect");
            }
        }
    }

    /**
     * reads data supplied by the XPort and returns a String
     * 
     * @return
     * @throws TCPCommunicationException
     */
    public String receiveString() throws TCPCommunicationException {
        String antwort = "";
        int length=255;
        byte[] in = new byte[length];
        
        try {
            dis.ready(); // check if data is available (actually redundant since
            // the calling method from the GUI class already made
            // this sure but better safe than sorry... :-)
            length = readBuffer(in,length); // call helper method to read from dis
            antwort= new String(in,0,length);
        } catch (Exception Ex) {
			connected = false;
            throw new TCPCommunicationException(Ex.getLocalizedMessage());
        }
        return antwort;
    }

    /**
     * this method will read from DataInputStream dis, convert the chars read
     * into ASCII and return its answer as a String
     * 
     * @return
     * @throws TCPCommunicationException
     */
    public synchronized int readBuffer(int[] gelesen,int length) throws TCPCommunicationException {
        int i=0; // init helper variables
        //byte [] gelesen = new byte[length.intValue()];
        
        try {
	        while ((dis.ready())& (i<length)){  // loop while data available
	        	if (gelesen!=null)
	        		gelesen[i++]=dis2.readUnsignedByte();// read from dis
	        	else 
	        		dis2.readUnsignedByte();// read from dis
	        }
	    } catch (Exception Ex) {
			connected = false;
	        throw new TCPCommunicationException(Ex.getLocalizedMessage());
	    }
        
        return i;
    }

    /**
     * this method will read from DataInputStream dis, convert the chars read
     * into ASCII and return its answer as a String
     * 
     * @return
     * @throws TCPCommunicationException
     */
    public synchronized int readBuffer(byte[] gelesen,int length) throws TCPCommunicationException {
        int i=0; // init helper variables
        //byte [] gelesen = new byte[length.intValue()];
        
        try {
	        while ((dis.ready())& (i<length)){  // loop while data available
	        	gelesen[i++]=(byte)dis2.readUnsignedByte();// read from dis
	
	        }
        } catch (Exception Ex) {
			connected = false;
            throw new TCPCommunicationException(Ex.getLocalizedMessage());
        }
        	
        return i;
    }
    
    
    /**
     * reads data supplied by the XPort and returns an array of bytes
     * 
     * @param length
     * @return
     * @throws TCPCommunicationException
     */
    public byte[] receiveByteArray(int length) throws TCPCommunicationException {
        byte[] antwort = { 0, // this method hardly differs from the string
                0, 0, 0, 0 }; // reading one, so same comments apply
        try {
            dis.ready();
        } catch (Exception Ex) {
			connected = false;
            throw new TCPCommunicationException(Ex.getLocalizedMessage());
        }
        try {
            antwort = readByteArrayBuffer(length);
        } catch (Exception Ex) {
			connected = false;
            throw new TCPCommunicationException(Ex.getLocalizedMessage());
        }
        return antwort;
    }

    /**
     * this method will read from DataInputStream dis and return its answer as
     * an array of bytes
     * 
     * @param length
     * @return
     * @throws TCPCommunicationException
     */
    public synchronized byte[] readByteArrayBuffer(int length)
            throws TCPCommunicationException {
        /*
         * this method again hardly differs from the string reading one, so same
         * comments apply here as well
         */

        int lesen = 0;
        byte gelesen[] = new byte[length];
        int i = 0;
        int timeout = 0;
        
        try {
	        while (!dis.ready() && timeout < 8) { // wait max 2 seconds for data
	            try {
	                wait(250);
	                timeout++;
	            } catch (InterruptedException IntEx) {
	            }
	        }
	        
	        if (timeout ==8) throw(new Exception("Timeout"));
	        
	        while (dis.ready()) {
	            while (i < length) {
	                // --Plymo-- 7.1.2005 "& 0xFF"
	                lesen = (dis.read()) & 0xFF;
	                gelesen[i] = (byte) lesen;
	                i++;
	            }
	        }
        } catch (Exception Ex) {
			connected = false;
            throw new TCPCommunicationException(Ex.getLocalizedMessage());
        }
        
        //System.out.println("readBytearray Ende");
        return gelesen;
    }

    /**
     * this class does all the actual TCP/IP connection setup work
     * 
     * @param ipa
     * @param port
     * @throws TCPCommunicationException
     */
    public void tcpip_connect(InetAddress ipa, int port) throws TCPCommunicationException {
        try {
            //  really establish a TCP connection (at last...)
            socket = new Socket(ipa.getHostAddress(), port);
        } catch (Exception Ex) {
            throw new TCPCommunicationException(Ex.getLocalizedMessage());
        }
        try { // set up a buffered stream to read from XPort and write to PC
            dis = new BufferedReader(new InputStreamReader(socket
                    .getInputStream()));
            dis2= new DataInputStream(socket.getInputStream());
        } catch (Exception Ex) {
            throw new TCPCommunicationException(Ex.getLocalizedMessage());
        }
        try { // set up a buffered stream to read from PC and write to XPort
        	dos= 		new DataOutputStream (socket.getOutputStream());
        	
        	/*            dos = new BufferedWriter(new OutputStreamWriter(socket
                    .getOutputStream())); */
        } catch (Exception Ex) {
            throw new TCPCommunicationException(Ex.getLocalizedMessage());
        }
    }

    /**
     * this method simply returns the value of DataInputStream dis' method
     * ready()
     * 
     * @return
     */
    public boolean DataReady() {
        try {
            return dis.ready();
        } catch (Exception Ex) {
            return false;
        }
    }
	
	/**
	 * returns connection-status
	 * @return
	 *		true if connection is established
	 */
	public boolean connected() {
		return connected;
	}
}
